package drds.server.net.packet;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 
 * @author 曹正辉<330937205@qq.com>
 * 
 */
public class StreamUtil {
	private static final long NULL_LENGTH = -1;
	private static final byte[] EMPTY_BYTES = new byte[0];

	public static final void read(InputStream inputStream, byte[] bytes, int offset, int length) throws IOException {
		for (int read = 0; length > 0;) {
			read = inputStream.read(bytes, offset, length);
			if (read < 0) {
				throw new EOFException();
			}
			offset += read;
			length -= read;
		}
	}

	public static final byte read(InputStream inputStream) throws IOException {
		int read = inputStream.read();
		if (read < 0) {
			throw new EOFException();
		}
		return (byte) (read & 0xff);
	}

	public static final int readInt2(InputStream inputStream) throws IOException {
		byte[] bytes = new byte[2];
		read(inputStream, bytes, 0, bytes.length);
		int $int = bytes[0] & 0xff;
		$int |= (bytes[1] & 0xff) << 8;
		return $int;
	}

	public static final int readInt3(InputStream inputStream) throws IOException {
		byte[] bytes = new byte[3];
		read(inputStream, bytes, 0, bytes.length);
		int $int = bytes[0] & 0xff;
		$int |= (bytes[1] & 0xff) << 8;
		$int |= (bytes[2] & 0xff) << 16;
		return $int;
	}

	public static final int readInt4(InputStream inputStream) throws IOException {
		byte[] bytes = new byte[4];
		read(inputStream, bytes, 0, bytes.length);
		int $int = bytes[0] & 0xff;
		$int |= (bytes[1] & 0xff) << 8;
		$int |= (bytes[2] & 0xff) << 16;
		$int |= (bytes[3] & 0xff) << 24;
		return $int;
	}

	public static final long readLong4(InputStream inputStream) throws IOException {
		byte[] bytes = new byte[4];
		read(inputStream, bytes, 0, bytes.length);
		long $long = (long) (bytes[0] & 0xff);
		$long |= (long) (bytes[1] & 0xff) << 8;
		$long |= (long) (bytes[2] & 0xff) << 16;
		$long |= (long) (bytes[3] & 0xff) << 24;
		return $long;
	}

	public static final long readLong8(InputStream inputStream) throws IOException {
		byte[] bytes = new byte[8];
		read(inputStream, bytes, 0, bytes.length);
		long $long = (long) (bytes[0] & 0xff);
		$long |= (long) (bytes[1] & 0xff) << 8;
		$long |= (long) (bytes[2] & 0xff) << 16;
		$long |= (long) (bytes[3] & 0xff) << 24;
		$long |= (long) (bytes[4] & 0xff) << 32;
		$long |= (long) (bytes[5] & 0xff) << 40;
		$long |= (long) (bytes[6] & 0xff) << 48;
		$long |= (long) (bytes[7] & 0xff) << 56;
		return $long;
	}

	public static final byte[] readWithLength(InputStream inputStream) throws IOException {
		int length = (int) readLength(inputStream);
		if (length <= 0) {
			return EMPTY_BYTES;
		}
		byte[] bytes = new byte[length];
		read(inputStream, bytes, 0, bytes.length);
		return bytes;
	}

	public static final void write(OutputStream outputStream, byte bytes) throws IOException {
		outputStream.write(bytes & 0xff);
	}

	public static final void writeInt2(OutputStream outputStream, int $int) throws IOException {
		byte[] bytes = new byte[2];
		bytes[0] = (byte) ($int & 0xff);
		bytes[1] = (byte) ($int >>> 8);
		outputStream.write(bytes);
	}

	public static final void writeInt3(OutputStream outputStream, int $int) throws IOException {
		byte[] bytes = new byte[3];
		bytes[0] = (byte) ($int & 0xff);
		bytes[1] = (byte) ($int >>> 8);
		bytes[2] = (byte) ($int >>> 16);
		outputStream.write(bytes);
	}

	public static final void writeInt4(OutputStream outputStream, int $int) throws IOException {
		byte[] bytes = new byte[4];
		bytes[0] = (byte) ($int & 0xff);
		bytes[1] = (byte) ($int >>> 8);
		bytes[2] = (byte) ($int >>> 16);
		bytes[3] = (byte) ($int >>> 24);
		outputStream.write(bytes);
	}

	public static final void writeLong4(OutputStream outputStream, long $long) throws IOException {
		byte[] bytes = new byte[4];
		bytes[0] = (byte) ($long & 0xff);
		bytes[1] = (byte) ($long >>> 8);
		bytes[2] = (byte) ($long >>> 16);
		bytes[3] = (byte) ($long >>> 24);
		outputStream.write(bytes);
	}

	public static final void writeLong8(OutputStream outputStream, long $long) throws IOException {
		byte[] bytes = new byte[8];
		bytes[0] = (byte) ($long & 0xff);
		bytes[1] = (byte) ($long >>> 8);
		bytes[2] = (byte) ($long >>> 16);
		bytes[3] = (byte) ($long >>> 24);
		bytes[4] = (byte) ($long >>> 32);
		bytes[5] = (byte) ($long >>> 40);
		bytes[6] = (byte) ($long >>> 48);
		bytes[7] = (byte) ($long >>> 56);
		outputStream.write(bytes);
	}

	public static final long readLength(InputStream inputStream) throws IOException {
		int length = inputStream.read();
		if (length < 0) {
			throw new EOFException();
		}
		switch (length) {
		case 251:
			return NULL_LENGTH;
		case 252:
			return readInt2(inputStream);
		case 253:
			return readInt3(inputStream);
		case 254:
			return readLong8(inputStream);
		default:
			return length;
		}
	}

	public static final void writeLength(OutputStream outputStream, long length) throws IOException {
		if (length < 251) {
			outputStream.write((byte) length);
		} else if (length < 0x10000L) {
			outputStream.write((byte) 252);
			writeInt2(outputStream, (int) length);
		} else if (length < 0x1000000L) {
			outputStream.write((byte) 253);
			writeInt3(outputStream, (int) length);
		} else {
			outputStream.write((byte) 254);
			writeLong8(outputStream, length);
		}
	}

	public static final void writeWithNull(OutputStream outputStream, byte[] bytes) throws IOException {
		outputStream.write(bytes);
		outputStream.write((byte) 0);
	}

	public static final void writeWithLength(OutputStream outputStream, byte[] bytes) throws IOException {
		int length = bytes.length;
		if (length < 251) {
			outputStream.write((byte) length);
		} else if (length < 0x10000L) {
			outputStream.write((byte) 252);
			writeInt2(outputStream, length);
		} else if (length < 0x1000000L) {
			outputStream.write((byte) 253);
			writeInt3(outputStream, length);
		} else {
			outputStream.write((byte) 254);
			writeLong8(outputStream, length);
		}
		outputStream.write(bytes);
	}

}